package com.shangyueshang.utils;

import com.shangyueshang.common.SysConstants;
import com.shangyueshang.security.GrantedAuthorityImpl;
import com.shangyueshang.security.JwtAuthenticationToken;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.authentication.CredentialsExpiredException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;

import javax.servlet.http.HttpServletRequest;
import java.util.*;

/**
 * @author: JokeyZheng
 * @email: zhengjingfeng@ruqimobility.com
 * @created: 2022/1/6 19:46
 * @version: v1.0.0
 * <p>
 * JWT工具类
 */
@Slf4j
public abstract class JwtTokenUtil {

    /**
     * 用户名称
     */
    private static final String USERNAME = Claims.SUBJECT;
    /**
     * 创建时间
     */
    private static final String CREATED = "created";
    /**
     * 权限列表
     */
    private static final String AUTHORITIES = "authorities";
    /**
     * 密钥
     */
    private static final String SECRET = "123@qwe";
    /**
     * 有效期12小时
     */
    private static final long EXPIRE_TIME = 12 * 60 * 60 * 1000;

    /**
     * 生成令牌
     *
     * @param authentication 用户数据
     * @return
     */
    public static String createToken(Authentication authentication) {
        Map<String, Object> claims = new HashMap<>(3);
        claims.put(CREATED, new Date());
        claims.put(AUTHORITIES, authentication.getAuthorities());
        claims.put(USERNAME, SecurityUtil.getUsername(authentication));
        return createToken(claims);
    }

    /**
     * 从数据声明生成令牌
     *
     * @param claims
     * @return
     */
    private static String createToken(Map<String, Object> claims) {
        Date expirationTime = new Date(System.currentTimeMillis() + EXPIRE_TIME);
        return Jwts.builder()
                .setClaims(claims)
                .setExpiration(expirationTime)
                .signWith(SignatureAlgorithm.HS512, SECRET)
                .compact();
    }

    /**
     * 根据令牌获取登录认证信息
     *
     * @param request
     * @return
     */
    public static Authentication getAuthenticationFromToken(HttpServletRequest request) {
        Authentication authentication = null;
        //获取令牌
        String token = getToken(request);
        if (token != null) {
            if (SecurityUtil.getAuthentication() == null) {
                Claims claims = getClaimsFromToken(token);
                if (claims == null) {
                    return null;
                }
                String username = claims.getSubject();
                if (username == null) {
                    return null;
                }
                if (isTokenExpired(token)) {
                    return null;
                }
                Object authors = claims.get(AUTHORITIES);
                List<GrantedAuthority> authorities = new ArrayList<>();
                if (authors instanceof List) {
                    for (Object o : (List) authors) {
                        authorities.add(new GrantedAuthorityImpl((String) ((Map) o).get(SysConstants.authority)));
                    }
                }
                authentication = new JwtAuthenticationToken(username, null, authorities, token);
            } else {
                if (validateToken(token, SecurityUtil.getUsername())) {
                    authentication = SecurityUtil.getAuthentication();
                }
            }
        }

        return authentication;

    }

    /**
     * 获取请求token
     *
     * @param request
     * @return
     */
    private static String getToken(HttpServletRequest request) {
        String token = request.getHeader(SysConstants.authorization);
        String tokenHead = SysConstants.bearer;
        if (token == null) {
            token = request.getHeader(SysConstants.token);
        } else if (token.contains(tokenHead)) {
            token = token.substring(tokenHead.length());
        }
        if ("".equals(token)) {
            token = null;
        }
        return token;
    }

    /**
     * 从令牌中获取用户名
     *
     * @param token
     * @return
     */
    public static String getUsernameFromToken(String token) {
        String username;
        try {
            Claims claims = getClaimsFromToken(token);
            username = claims.getSubject();
        } catch (Exception e) {
            e.printStackTrace();
            username = null;
        }
        return username;
    }

    /**
     * 校验令牌是否过期
     *
     * @param token
     * @return
     */
    private static boolean isTokenExpired(String token) {
        try {
            Claims claims = getClaimsFromToken(token);
            Date expiration = claims.getExpiration();
            return expiration.before(new Date());
        } catch (Exception e) {
            return false;
        }
    }

    /**
     * 从令牌token中获取数据声明
     *
     * @param token
     * @return
     */
    private static Claims getClaimsFromToken(String token) {
        Claims claims;
        try {
            claims = Jwts.parser().setSigningKey(SECRET).parseClaimsJws(token).getBody();
        } catch (ExpiredJwtException e) {
            //fixme 过期时抛出异常
            throw new CredentialsExpiredException("User credentials have expired");
        } catch (Exception ex) {
            ex.printStackTrace();
            claims = null;
        }
        return claims;
    }

    /**
     * 验证令牌
     *
     * @param token
     * @param username
     * @return
     */
    private static boolean validateToken(String token, String username) {
        String usernameFromToken = getUsernameFromToken(token);
        return username.equals(usernameFromToken) && !isTokenExpired(token);
    }

}
